from SimPy.Simulation import *
## from SimPy.SimulationTrace import *
from random import expovariate,seed
import random as ran
import sys

class Task(Process):
    """ A computer  task  requires at least
    one use of the CPU and possibly accesses to a
    disk drive."""
    completed = 0
    def execute(self):
        start_time = now()
        # while Task.completed < maxCompletions:
        while now()-start_time<samplingIntrl:        
            self.debug(" starts thinking")
            thinktime = ran.expovariate(1.0/MeanThinkTime)
            yield hold,self,thinktime
            self.debug(" request cpu")
            yield request,self,cpu
            self.debug(" got cpu")
            CPUtime=ran.expovariate(1.0/MeanCPUTime)
            yield hold,self,CPUtime
            yield release,self,cpu
            self.debug(str(Task.completed)+" finish cpu")
            Task.completed += 1
        self.debug(" completed %d tasks"%(Task.completed,))
        

    def debug(self,message):
        FMT="%9.3f %s %s"
        if DEBUG:
            print FMT%(now(),self.name,message)


class Clerk(Process):
    def work(self,myOffice):
        #start_time = now()
        #while now()-start_time<samplingIntrl: 
        yield request,self,myOffice,100 #leave desk
        # print now(),[x.name for x in myOffice.activeQ]
        yield hold,self,samplingIntrl
        yield release,self,myOffice #return to desk

class Generator(Process):        
    def execute(self,numberOfUsers,samplingIntrl):
        completedBefore=0
        timeBefore=now()
        for j in range(len(numberOfUsers)):            
            # configuring number of servers
            # e.g. for 2 resource we need 20-2 occupiers
            occupiers=maxServers-numberOfServers[j]
            for i in range(occupiers):
                c=Clerk('Clerk %s'%i)
                activate(c,c.work(cpu))            

            # configuring number of customers
            for i in range(numberOfUsers[j]):
                t = Task(name="Job "+'i')
                activate(t,t.execute())
                
            # waiting samplingIntrl secs
            yield hold,self,samplingIntrl                         
            completed_cur = (Task.completed - completedBefore)
            completed.append(completed_cur)
            throughput_ = completed_cur/(now()-timeBefore)            
            # self.debug("interval "+str(now()/samplingIntrl)+ ",["+str(timeBefore)+","+ str(now()) +"]"+
            #           " completed "+str(completed_cur) + " throughput "+str(throughput_))             
            throughput.append(throughput_)
            print "%0.9f," % self.calc_utilization(j+1), 
                                    
            completedBefore = Task.completed
            timeBefore=now()          

    def debug(self,message):
        FMT="%9.3f %s %s"
        #if DEBUG:
        print FMT%(now(),self.name,message)


    # min = 1,2,...; 
    # calc_utilization(1) means utilization of minute 1 computed after the minute
    def calc_utilization(self, min):
        # check if and min<=len(numberOfServers)
        # this cond is to dicard the data we get during cooling time, which we dont have 
        # Clerk data for
        acc=0;
        t_old=(min-1)*samplingIntrl;
        for item in cpu.actMon:
            t=item[0]
            util=item[1]    
            if t>(min-1)*samplingIntrl: 
                acc+=(t-t_old)*util;      
                t_old=t
            if t>min*samplingIntrl:                
                break

        occupiers = maxServers-numberOfServers[min-1] # dont calculate occupiers in this model
        agg_util=acc/samplingIntrl-occupiers
        utilization_agg.append(agg_util) # numberOfServers[min-1]
        util = agg_util/numberOfServers[min-1]
        utilization.append(util)
        return util
        #raise Exception('method did not return after for loop')

    
## Experiment data -------------------------

# configs
ran.seed(111113333)
samplingIntrl=60
MaxrunTime = 20000.0
DEBUG = False


maxServers=20
if len(sys.argv)==1:
    # estimates (inputs to simulation) 
    MeanThinkTime = 1.0 ## seconds
    MeanCPUTime = 0.10    ## seconds
    # inputs
    numberOfUsers = [52,52]
    numberOfServers= [16,4] 
else:
    MeanThinkTime = float(sys.argv[1]) ## seconds
    MeanCPUTime = float(sys.argv[2])    ## seconds
    numberOfUsers = [int(i) for i in sys.argv[3].split(',')]
    # print "len numberOfUsers %d"%(len(numberOfUsers))
    numberOfServers= [int(i) for i in sys.argv[4].split(',')]

# outputs
completed=[] 
throughput=[]
utilization_agg=[]
utilization = []


cpu  = Resource(name='cpu', capacity=maxServers , monitored=True, qType=PriorityQ) # preemptable=True
initialize()
g = Generator('gen')
activate(g,g.execute(numberOfUsers=numberOfUsers, samplingIntrl=samplingIntrl))

simulate(until = MaxrunTime)

## result=main()

## Analysis/output -------------------------
# print 'central server'
# print '%7.4f: CPU rate = %7.4f tasks per second'%result
            

#min=1;
#acc=0;
#t_old=0;
#for item in cpu.actMon:
#    t=item[0]
#    util=item[1]    
#    # second cond is to dicard the data we get during cooling time, which we dont have 
#    # Clerk data for
#    if t>min*samplingIntrl and min<=len(numberOfServers):               
#        try:
#            # print "servers %d min %d"%(len(numberOfServers),min)
#            occupiers = maxServers-numberOfServers[min-1] # dont calculate occupiers in this model
#        except:
#            print "Unexpected error:", sys.exc_info()[0]
#            raise
#
#        agg_util=acc/samplingIntrl-occupiers
#        utilization_agg.append(agg_util) # numberOfServers[min-1]
#        utilization.append(agg_util/numberOfServers[min-1])
#        min+=1        
#        acc=0
#    acc+=(t-t_old)*util;      
#    t_old=t
#    
#    
## print utilization_agg
## print "len util %d"%(len(utilization))
#print ",".join(["%0.9f" % i for i in utilization])

    
# print throughput
# print cpu.actMon
# print cpu.actMon.timeAverage()
# print cpu.waitMon.timeAverage()



